﻿namespace KinonekoSoftware.Foundation
{
    public abstract class ContextBase
    {
        public const string Key_Value  = "value";
        public const string Key_Value2  = "value2";
        public const string Key_Parameter  = "param";
        public const string Key_Parameter2  = "param2";
        
        public bool Has(string key) => Parameter.ContainsKey(key);

        public int GetInteger(string key)
        {
            if (Parameter.TryGetValue(key, out var np))
            {
                if (np is null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (np is not NumericArgument np2)
                {
                    throw new InvalidCastException(nameof(np));
                }

                return np2.Value;
            }

            throw new KeyNotFoundException(nameof(key));
        }
        
        public bool GetBoolean(string key)
        {
            if (Parameter.TryGetValue(key, out var bp))
            {
                if (bp is null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (bp is not BooleanArgument bp2)
                {
                    throw new InvalidCastException(nameof(bp));
                }

                return bp2.Value;
            }

            throw new KeyNotFoundException(nameof(key));
        }
        
        public object GetObject(string key)
        {
            if (Parameter.TryGetValue(key, out var op))
            {
                if (op is null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (op is not ObjectArgument op2)
                {
                    throw new InvalidCastException(nameof(op));
                }

                return op2.Value;
            }

            throw new KeyNotFoundException(nameof(key));
        }

        public T GetObject<T>(string key)
        {
            
            if (Parameter.TryGetValue(key, out var op))
            {
                if (op is null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (op is not ObjectArgument<T> op2)
                {
                    throw new InvalidCastException(nameof(op));
                }

                return op2.Value;
            }

            throw new KeyNotFoundException(nameof(key));
        }

        public string GetString(string key)
        {
            if (Parameter.TryGetValue(key, out var sp))
            {
                if (sp is null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (sp is not StringArgument sp2)
                {
                    throw new InvalidCastException(nameof(sp));
                }

                return sp2.Value;
            }

            throw new KeyNotFoundException(nameof(key));
        }

        public void Set(string key, int value)
        {
            var np = new NumericArgument { Value = value };
            Parameter[key] = np;
        }





        public void Set(string key, bool value)
        {
            var np = new BooleanArgument { Value = value };
            Parameter[key] = np;
        }


        public void Set(string key, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return;
            }
            
            var np = new StringArgument { Value = value };
            Parameter[key] = np;
        }

        public void SetObject(string key, object value)
        {
            if (value is null)
            {
                return;
            }
            
            var np = new ObjectArgument { Value = value };
            Parameter[key] = np;
        }
        
        public void Set<T>(string key, T value)
        {
            if (value is null)
            {
                return;
            }
            
            var np = new ObjectArgument<T> { Value = value };
            Parameter[key] = np;
        }
        
        public void Set(string key, ImplicitArgument implicitArgument)
        {
            if (implicitArgument is null)
            {
                return;
            }
            
            Parameter[key] = implicitArgument;
        }

        internal Dictionary<string, ImplicitArgument> Parameter { get; init; }
    }
}